/*******************************************************************************
 * @file Copyright 2009 The CREATE Lab at CMU
 * 
 *       This file is part of JubJub, an open source project.
 *       http://code.google.com/p/jubjub/
 * 
 *       Redistribution and use in source and binary forms, with or without
 *       modification, are permitted provided that the following conditions are
 *       met:
 * 
 *       Redistributions of source code must retain the above copyright notice,
 *       this list of conditions and the following disclaimer.
 * 
 *       Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in the
 *       documentation and/or other materials provided with the distribution.
 * 
 *       Neither the name of Carnegie Mellon nor the names of its contributors
 *       may be used to endorse or promote products derived from this software
 *       without specific prior written permission.
 * 
 *       THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 *       "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 *       LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 *       PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 *       HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 *       SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 *       LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 *       DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 *       THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *       (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 *       OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *******************************************************************************/

package edu.cmu.ri.createlab.jj.blocks;

import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;

import edu.cmu.ri.createlab.jj.JubJub.EmptyStringException;
import edu.cmu.ri.createlab.jj.blocks.BlockDefs.*;
import edu.cmu.ri.createlab.jj.visitors.*;
import edu.cmu.ri.createlab.jj.projects.*;

/**
 * 
 * @author Erik Pasternak
 *
 * The BlockBlock class is the main way of interacting with code. BlockBlock blocks
 * are built from a sequential list of of references to other blocks that represent
 * the code for that block. They also maintain a list of parameters, returns, and
 * variables that they use. 
 * 
 * Like CodeBlock BlockBlock has two call types, METHOD style
 * blocks have no or one return and can therefore be generated as a function/method
 * when compiled to Java. INLINE style blocks have more than one return and must be
 * copied and pasted wherever they are used.
 * 
 * Be advised, the editor must register/unregister blocks with their class.
 */
public class BlockBlock implements BlockInterface, CallableBlockInterface
{
   
   /**
    * \defgroup BlockVars These variables are common to all BlockInterface types.
    */
   /*@{*/

   ///The name given to this block. CodeBlock and BlockBlock types should have unique names
   private String name;
   ///The JJClass which these blocks belong to and where they will be written to
   ///when output as code/xml
   private JJClass classOwner;
   ///What type of block this is from the list of BlockTypes
   private BlockTypes type;
   ///A unique ID for this block
   private int id;
   ///A list of RefBlock instances that reference this block.
   private List<RefBlock> refs;
   ///Whether this block is still valid or not
   private boolean valid;
   ///Comment for this block
   private String comment;
   ///whether this block has an error
   private boolean error;
   
   /*@}*/
   
   /**
    * \defgroup CallableVars These variables are common to CodeBlocks, BlockBlocks, and VarBlocks.
    */
   /*@{*/
   ///This is a list of the access keywords that can go in front of a definition.
   ///So far this includes private, public, static, final, and protected. Up to
   ///3 access types can be included (ex. "public static final"). This list is
   ///defined in AccessTypes
   private AccessTypes[] access;
   ///This is to provide easy boolean access to the access types for quick lookup.
   ///The order is PUBLIC, PRIVATE, PROTECTED, FINAL, STATIC and should be referenced
   ///by calling BlockDefs.AccessTypes.TYPE.getIndex
   private boolean[] isAccess = {false, false, false, false, false};

   
   ///The name used when calling/defining this block in code.
   private String callName;
   /*@}*/
   
   /**
    * \defgroup CodeVars These variables are common to CodeBlock and BlockBlock types.
    */
   /*@{*/
   
   ///This determines whether a block is coded using an inline style or a method
   ///call, defined in CallTypes.
   private CallTypes callType;
   ///A list of the parameters passed into the block in VarBlock format.
   private List<VarBlock> params;
   ///A list of the returns passed out of the block in VarBlock format.
   private List<RefBlock> returns;
   
   ///A string with the full package or object name so that methods can be called explicitly.
   ///This should end with a '.' and is mainly for use with external code and objects, such
   ///as built in Java functions (eg. System.out.println).
   ///Two examples of prefixes would be "System.out." and "myFinch."
   private String prefix;
   ///Whether or not this block is a constructor
   private boolean constructor;
     
   ///swapNames is a temporary list of names to replace variables with.
   ///It should be set by the visitor whenever it processes a reference to this
   ///block and the names here will be used to replace variable names for that
   ///instance. TaggedCodePieces that reference a variable will contain the index
   ///of their variable, which should be in the order of the params then returns lists.
   private String[] swapNames;
   
   /*@}*/
   
   /**
    * \defgroup BlockBlockVars These variables are used only by BlockBlock.
    */
   /*@{*/
   
   /**
    * A list of internal variables that the code uses. CodeBlock does not have
    * this because its internal vars should be part of the code, while BlockBlock
    * needs to list all the vars it uses and have them created so they can be used
    * by reference in the 'code' below.
    */
   private List<VarBlock> vars;
   
   /**
    * BlockBlock code is a list of RefBlock objects that, when written sequentially,
    * make up a unit of code. The RefBlocks <i>should</i> only be made up of references
    * to CodeBlock and BlockBlock objects, since variables for this block are defined
    * above. At a higher level the software should also check to ensure all vars used
    * in calls (RefBlocks in params or returns fields in a RefBlock) are in scope.
    */
   private List<RefBlock> code;
   
   /*@}*/
   
   /**
    * 
    * @param name A unique block name for this block.
    * @param owner Which JJClass it belongs to
    * @param ID A globally unique identifier
    * @throws EmptyStringException
    * 
    * Creates a BlockBlock with only the necessary settings.
    * 
    *  This creates a new BlockBlock without AccessTypes and sets most of its
    *  internal lists to null, meaning they still have to be set by the creating
    *  piece of code. params, returns, and code all have to be created
    *  externally. refs and vars start as empty lists that should be added to. 
    */
   public BlockBlock(String name, JJClass owner, int ID)
      throws Exception
   {
      this.setName(name);
      this.valid = true;
      this.classOwner = owner;
      this.id = ID;
      this.type = BlockTypes.BLOCK;
      this.callType = CallTypes.METHOD;
      this.setAccess(null);
      this.params = new LinkedList<VarBlock>();
      this.returns = new LinkedList<RefBlock>();
      this.vars = new LinkedList<VarBlock>();
      this.code = new LinkedList<RefBlock>();
      this.refs = new LinkedList<RefBlock>();
      this.prefix = null;
      this.comment = "";
   }
   
   /**
    * 
    * @param name A unique block name for this block.
    * @param owner Which JJClass it belongs to
    * @param ID A globally unique identifier
    * @param callType whether this block is INLINE or METHOD type
    * @throws EmptyStringException
    * 
    * Creates a BlockBlock with only the necessary settings.
    * 
    *  This creates a new BlockBlock without AccessTypes and sets most of its
    *  internal lists to null, meaning they still have to be set by the creating
    *  piece of code. params, returns, and code all have to be created
    *  externally. refs and vars start as empty lists that should be added to. 
    *  Use this constructor when making INLINE blocks.
    */
   public BlockBlock(String name, JJClass owner, int ID, CallTypes callType)
      throws Exception
   {
      this.setName(name);
      this.valid = true;
      this.classOwner = owner;
      this.id = ID;
      this.type = BlockTypes.BLOCK;
      this.callType = callType;
      this.setAccess(null);
      this.params = new LinkedList<VarBlock>();
      this.returns = new LinkedList<RefBlock>();
      this.vars = new LinkedList<VarBlock>();
      this.code = new LinkedList<RefBlock>();
      this.refs = new LinkedList<RefBlock>();
      this.prefix = null;
      this.comment = "";
   }

   /**
    * 
    * @param name A unique block name for this block.
    * @param owner Which JJClass it belongs to
    * @param ID A globally unique identifier
    * @param access The access keywords to use with this block
    * @throws EmptyStringException
    * 
    *  Creates a BlockBlock with only the necessary settings and access.
    * 
    *  This creates a new BlockBlock and sets most of its
    *  internal lists to null, meaning they still have to be set by the creating
    *  piece of code. params, returns, and code all have to be created
    *  externally. refs and vars start as empty lists that should be added to. 
    */
   public BlockBlock(String name, JJClass owner, int ID, AccessTypes[] access)
      throws Exception
   {
      this.setName(name);
      this.valid = true;
      this.classOwner = owner;
      this.id = ID;
      this.type = BlockTypes.BLOCK;
      this.callType = CallTypes.METHOD;
      this.setAccess(access);
      this.params = new LinkedList<VarBlock>();
      this.returns = new LinkedList<RefBlock>();
      this.vars = new LinkedList<VarBlock>();
      this.code = new LinkedList<RefBlock>();
      this.refs = new LinkedList<RefBlock>();
      this.prefix = null;
      this.comment = "";
   }
   

   /**
    * 
    * @param name A unique block name for this block.
    * @param owner Which JJClass it belongs to
    * @param ID A globally unique identifier
    * @param access The access keywords to use with this block
    * @param params A list of VarBlock parameters this block uses
    * @param returns A list of VarBlock returns this block uses
    * @throws EmptyStringException
    * 
    * Creates a BlockBlock with some settings.
    * 
    *  This creates a new BlockBlock with many of its internal lists passed
    *  in as arguments. The code still has to be created and set by an external
    *  piece of code. params, returns, and code all have to be created
    *  externally. refs and vars start as empty lists that should be added to. 
    */
   public BlockBlock(String name, JJClass owner, int ID, AccessTypes[] access,
         LinkedList<VarBlock> params, LinkedList<RefBlock> returns)
      throws Exception
   {
      this.setName(name);
      this.valid = true;
      this.classOwner = owner;
      this.id = ID;
      this.type = BlockTypes.BLOCK;
      this.callType = CallTypes.METHOD;
      this.setAccess(access);
      this.params = new LinkedList<VarBlock>();
      this.returns = new LinkedList<RefBlock>();
      this.setParams(params);
      this.setReturns(returns);
      this.vars = new LinkedList<VarBlock>();
      this.code = new LinkedList<RefBlock>();
      this.refs = new LinkedList<RefBlock>();
      this.prefix = null;
      this.comment = "";
   }

   /**
    * \defgroup BlockMethods These methods are common to all blocks.
    */
   /*@{*/
   
   ///Sets the comment for this block
   public void setComment(String comment)
   {
      this.comment = comment;
   }
   ///Gets the comment for this block
   public String getComment()
   {
      return comment;
   }
   
   ///Return whether this block is still valid or if it is deleted pending
   ///removal of all its refs.
   public boolean isValid()
   {
      return valid;
   }
   ///Sets this block to not Valid and updates all its refs
   public void deleteBlock()
   {
      valid = false;   
      this.updateRefs();
   }
   
   ///Sets this block to Valid and updates all its refs
   public void undeleteBlock()
   {
      valid = true;
      this.updateRefs();
   }

   ///registers the block with its current class
   public void register()
      throws BlockException
   {
      if(this.classOwner != null)
      {
         if(this.callType == CallTypes.INLINE)
            this.classOwner.addInlineBlock(this);
         else this.classOwner.addMethodBlock(this);
      }
   }
   
   ///unregisters the block from its class.
   public void unregister()
   {
      if(this.classOwner != null)
      {
         if(this.callType == CallTypes.INLINE)
            this.classOwner.removeInlineBlock(this);
         else this.classOwner.removeMethodBlock(this);
      }
      this.setOwner(null);
   }
   
   ///Return whether this block currently has an error
   public boolean hasError()
   {
      return error;
   }
   
   ///check the error state for this block
   public void checkError()
   {
      Iterator<VarBlock> vIt = this.params.iterator();
      while(vIt.hasNext())
      {
         if(vIt.next().hasError())
         {
            this.error = true;
            if(this.classOwner != null)
               this.classOwner.checkError();
            return;
         }
      }
      Iterator<RefBlock> it = this.returns.iterator();
      while(vIt.hasNext())
      {
         if(vIt.next().hasError())
         {
            this.error = true;
            if(this.classOwner != null)
               this.classOwner.checkError();
            return;
         }
      }
      it = this.code.iterator();
      while(it.hasNext())
      {
         if(it.next().hasError())
         {
            this.error = true;
            if(this.classOwner != null)
               this.classOwner.checkError();
            return;
         }
      }
      if(this.error)
      {
         this.error = false;
         if(this.classOwner != null)
            this.classOwner.checkError();
      }
   }
   
   ///This makes it easier for the garbage collector to remove this block
   ///by ensuring all the references it contains are removed.
   ///Note that a destroyed block should be removed from its class first.
   public void destroyBlock()
   {      
      ///First remove all the code refs in this block
      Iterator<RefBlock> rbIt;
      rbIt = this.code.iterator();
      while(rbIt.hasNext())
      {
         rbIt.next().remove();
      }
      this.code.clear();
      
      ///Now delete all the parameters and returns
      Iterator<VarBlock> it;
      if(this.params != null)
      {
         it = this.params.iterator();
         while(it.hasNext())
         {
            it.next().destroyBlock();
         }
         this.params.clear();
      }
      if(this.returns != null)
      {
         Iterator<RefBlock> rit = this.returns.iterator();
         while(rit.hasNext())
         {
            rit.next().remove();
         }
         this.returns.clear();
      }
      it = this.vars.iterator();
      while(it.hasNext())
      {
         it.next().destroyBlock();
      }
      this.vars.clear();
      ///Finally, clear all references to this block
      rbIt = this.refs.iterator();
      while(rbIt.hasNext())
      {
         rbIt.next().clearBlock();
      }
      this.refs.clear();
   }
   
   
   ///Accepts a BlockVisitorInterface and visits itself
   public BVReturn accept(BlockVisitorInterface bv)
   {
      bv.visit(this);
      return null;
   }
   
   ///returns the JJClass that owns this block
   
   public JJClass getOwner()
   {
      return classOwner;
   }

   ///returns the id for this block
   
   public int getID()
   {
      return id;
   }

   ///returns the name of this block
   
   public String getName()
   {
      return name;
   }

   ///returns the BlockTypes type of this block
   
   public BlockTypes getType()
   {
      return type;
   }

   ///sets the JJClass that owns this block
   
   public void setOwner(JJClass owner)
   {
      this.classOwner = owner;
//      if(this.classOwner != null)
//      {
//         if(this.callType == CallTypes.INLINE)
//            this.classOwner.removeInlineBlock(this);
//         else this.classOwner.removeMethodBlock(this);
//      }
//      this.classOwner = owner;
//      try
//      {
//         if(this.callType == CallTypes.INLINE)
//            this.classOwner.addInlineBlock(this);
//         else this.classOwner.addMethodBlock(this);
//      }catch(Exception e)
//      {
//         System.err.println(e.getMessage());
//      }
      
   }

   ///sets the name of this block as well as its callName.
   
   public void setName(String name) throws EmptyStringException
   {
      ///If the name is of 0 length we've made a mistake and need to throw
      ///an exception.
      if(name.length() == 0) 
      {
         throw new EmptyStringException("Empty string name");
      }
      this.name = name;
      ///The callname should be the same as the name with some changes to make it
      ///Java friendly
      this.callName = name.replaceAll("[^a-z&&[^A-Z]&&[^0-9]&&[^_]]+", "_");
      if(!(this.callName.substring(0, 1).matches("[a-zA-z]")))
      {
         this.callName = "a" + this.callName;
      }
   }
   
   ///Adds a RefBlock to the list of references
   
   public void addRef(RefBlock ref)
   {
      if(refs.contains(ref)) return;
      refs.add(ref);
   }

   ///Removes the given RefBlock from the list of references
   
   public void remRef(RefBlock ref)
   {
      refs.remove(ref);
   }
   
   ///Returns a list of RefBlock types that reference this block
   public List<RefBlock> getRefs()
   {
      return refs;
   }

   ///Updates all the RefBlock references to this block
   
   public void updateRefs()
   {
      Iterator<RefBlock> it = refs.iterator();
      while(it.hasNext())
      {
         it.next().update();
      }
   }
   
   /*@}*/
   
   /**
    * \defgroup InstanceBlocks These methods are common to CodeBlock, BlockBlock, and VarBlock types.
    */
   /*@{*/
   
   ///Returns the list of AccessTypes keywords, may be null. Access should be null for
   ///INLINE type CodeBlocks and BlockBlocks
   public AccessTypes[] getAccess()
   {
      return access;
   }
   
   /**
    * @param access
    * @throws BlockException
    * 
    * Sets the list of AccessTypes keywords. May be null.
    * This will also check to ensure access types aren't being set for an INLINE
    * style block and that only one of public/private/protected is set. It will
    * also set the appropriate boolean flags for quick access to access settings.
    */
   public void setAccess(AccessTypes[] access) throws Exception
   {
      AccessTypes[] tempAccess = this.access;
      boolean[] tempIsAccess = this.isAccess.clone();
      ///A block becomes INLINE if it has more than one return, and since we
      ///can't create methods with more than one return it must be INLINE if
      ///it has multiple returns and vice versa.
      if(this.callType == CallTypes.INLINE && access != null)
      {
         throw new BlockException("Block cannot have multiple returns and have access types.");
      }
      ///Set the access keywords
      this.access = access;
      ///Set the boolean array to false to start
      for(int i = 0; i < this.isAccess.length; i++)
      {
         this.isAccess[i] = false;
      }
      ///Now set true any access keywords we added 
      if(access != null)
      {
         for(int i = 0; i < access.length; i++)
         {
            this.isAccess[access[i].getIndex()] = true;
         }
         ///You can only have one of the three visibility access types
         if((this.isAccess[AccessTypes.PUBLIC.getIndex()] && this.isAccess[AccessTypes.PRIVATE.getIndex()])
            || (this.isAccess[AccessTypes.PUBLIC.getIndex()] && this.isAccess[AccessTypes.PROTECTED.getIndex()])
            || (this.isAccess[AccessTypes.PRIVATE.getIndex()] && this.isAccess[AccessTypes.PROTECTED.getIndex()]))
         {
            ///so if we improperly set one of these undo changes and throw an exception
            this.access = tempAccess;
            this.isAccess = tempIsAccess;
            throw new BlockException("Can only have one of the following access types public/private/protected.");
         }
      }
      return;
   }
   
   ///returns whether or not this has the public keyword
   public boolean isPublic()
   {
      return isAccess[AccessTypes.PUBLIC.getIndex()];
   }

   ///returns whether or not this has the private keyword
   public boolean isPrivate()
   {
      return isAccess[AccessTypes.PRIVATE.getIndex()];
   }

   ///returns whether or not this has the protected keyword
   public boolean isProtected()
   {
      return isAccess[AccessTypes.PROTECTED.getIndex()];
   }

   ///returns whether or not this has the static keyword
   public boolean isStatic()
   {
      return isAccess[AccessTypes.STATIC.getIndex()];
   }

   ///returns whether or not this has the final keyword
   public boolean isFinal()
   {
      return isAccess[AccessTypes.FINAL.getIndex()];
   }
   
   /*@}*/
   
   
   /**
    * \defgroup CodeMethods These methods are common to CodeBlocks and BlockBlocks.
    */
   /*@{*/
   
 ///Returns the prefix for this CodeBlock
   public String getPrefix()
   {
      return prefix;
   }
   
   ///Sets the prefix for this CodeBlock
   public void setPrefix(String s)
   {
      prefix = s;
   }
   
   //Sets whether this block is a constructor or not.
   public void setConstructor(boolean constructor)
      throws Exception
   {
      if(constructor && !isPublic() && returns != null)
      {
         throw new BlockException("Constructors must be public and have no returns.");
      }
      this.constructor = constructor;
   }

   public boolean isConstructor()
   {
      return constructor;
   }
   
   ///returns the callName for this block
   public String getCallName()
   {
      return callName;
   }
   
   ///Checks the given string against the callName for this block
   public boolean isSameCallName(String name)
   {
      return this.callName.equals(name);
   }
   
   ///Returns the type of call, either INLINE or METHOD.
   ///Defined in CallTypes
   public CallTypes getCallType()
   {
      return callType;
   }
   
   ///returns the list of parameters as a VarBlock list
   public List<VarBlock> getParams()
   {
      return params;
   }
   
   ///returns the number of params
   public int getNumParams()
   {
      return params.size();
   }
   
   ///sets the list of paramaters. This should only be called at creation.
   private void setParams(List<VarBlock> params)
   {
      if(params != null)
      {
         Iterator<VarBlock> it = params.iterator();
         while(it.hasNext())
            this.addParam(it.next());
      }
   }
   
   ///Adds a parameter to its list
   public void addParam(VarBlock param)
   {
      this.params.add(param);
   }
   
   ///Adds a parameter to its list
   public void addParam(VarBlock param, int index)
   {
      this.params.add(index, param);
   }
   
   ///removes the specified parameter from the list
   public void remParam(VarBlock param)
   {
      this.params.remove(param);
   }
   
   
   ///sets the list of returns, it is up to the editor to decide if
   ///multiple returns are allowed or not.
   private void setReturns(List<RefBlock> returns)
      throws BlockException
   {      
      if(returns != null)
      {
         Iterator<RefBlock> it = returns.iterator();
         while(it.hasNext())
            this.addReturn(it.next());
      }
   }
   
   ///Adds the given variable as a return
   ///It's up to the editor to decide if more than one return is acceptable
   public void addReturn(RefBlock ret)
   {
      returns.add(ret);
   }
   
   ///Adds the given variable as a return at the given index
   ///It's up to the editor to decide if more than one return is acceptable
   public void addReturn(RefBlock ret, int index)
   {
      returns.add(index, ret);
   }
   
   ///removes the specified return from the list
   public void remReturn(VarBlock ret)
   {
      this.returns.remove(ret);
   }
   
   ///returns the list of returns as a RefBlock list
   public List<RefBlock> getReturns()
   {
      return returns;
   }
   
   ///returns the number of returns
   public int getNumReturns()
   {
      return returns.size();
   }

   
   ///Sets callType to the specified CallTypes type for the function.
   ///If the type is INLINE this will set the access keywords to null.
   ///This should only be done at creation currently, but later support
   ///may allow this to happen at other times. Be sure to add the block
   ///to the correct list in its class.
   public void setCallType(CallTypes type)
   {
      ///if we set this block to be inline remove its access types
      if(type == CallTypes.INLINE && this.access != null)
      {
         this.access = null;
      }
      this.callType = type;
   }

   ///sets the names to be used when outputting the code when different
   ///variable names should be swapped in (mainly INLINE types).
   public void setSwaps(String[] names)
   {
      swapNames = names;
   }
   
   ///Returns the swapNames that are currently in use.
   public String[] getSwaps()
   {
      return swapNames;
   }
   
   /*@}*/

   ///Adds a var to the end of the vars list
   public void addVar(VarBlock var)
   {
      this.vars.add(var);
   }
   
   ///inserts a var at the specified point in the vars list
   public void insertVar(VarBlock var, int index)
   {
      this.vars.add(index, var);
   }
   
   ///removes the specified var from the vars list
   public void remVar(VarBlock var)
   {
      this.vars.remove(var);
   }
   
   ///removes the var at the specified index
   public void remVar(int index)
   {
      this.vars.remove(index);
   }

   ///returns the list of vars
   public List<VarBlock> getVars()
   {
      return vars;
   }
   
   ///returns the var at the specified index
   public VarBlock getVar(int index)
   {
      return this.vars.get(index);
   }

   ///add a block to the code
   public void addCode(RefBlock block)
   {
      if(block != null) 
      {
         this.code.add(block);
         block.setOwner(this);
      }
   }

   ///add a block to the code at the specified location
   public void addCode(RefBlock block, int index)
   {
      if(block != null) 
      {
         this.code.add(index, block);
         block.setOwner(this);
      }
   }
   
   ///adds a chunk of code at the specified location
   public void addCode(RefBlock[] blocks, int index)
   {
      if(blocks != null)
      {
         int offset = 0;
         for(int i = 0; i < blocks.length; i++)
         {
            if(blocks[i] != null) 
            {
               this.code.add(index+offset,blocks[i]);
               blocks[i].setOwner(this);
               offset++;
            }
         }
      }
   }
   
   ///removes the code specified
   public void remCode(RefBlock block)
   {
      this.code.remove(block);
   }
   
   ///removes the code at the specified location
   public void remCode(int index)
   {
      this.code.remove(index);
   }
   

   ///returns the code list
   public List<RefBlock> getCode()
   {
      return this.code;
   }
   
   ///Returns a readable String representation of the Block
   public String toString()
   {
      String s = "";
      s += "Type:" + type.toString();
      s += " ID:" + id;
      s += " Name:" + name;
      if(prefix == null) s += " CallName:" + callName;
      else s += " CallName:" + prefix + callName;
      s += " CallType:" + callType;
      s += " Owner:" + classOwner;
      s += " Access:";
      if(access != null)
         for(int i = 0; i < access.length; i++)
            s += access[i].toString() + " ";
      s+= " NumRefs:" + refs.size();
      s += "\nParams:";
      if(params != null)
        for(int i = 0; i < params.size(); i++)
           s += params.get(i).getCallName() + ", ";
      s += "\nReturns:";
      if(returns != null)
        for(int i = 0; i < returns.size(); i++)
           s += returns.get(i).getName() + ", ";
      s += "\nVars:";
      if(vars != null)
         for(int i = 0; i < vars.size(); i++)
            s += vars.get(i).getCallName() + ", ";
      s += "\nCode:";
      if(code != null)
         for(int i = 0; i < code.size(); i++)
            s += code.get(i).getName() + " -> ";
      return s;
   }
   
   ///Some test code for BlockBlock.
   public static void main(String[] args)
   {
      BlockBlock bb1, bb2, bb3, bb4, bb5;
      ///Create an inline bit of code
      CodeBlock cb1, cb2, cb3, cb4;
      LinkedList<TaggedCodePiece> tc1 = new LinkedList<TaggedCodePiece>();
      tc1.add(new TaggedCodePiece("x",0));
      tc1.add(new TaggedCodePiece(" = "));
      tc1.add(new TaggedCodePiece("point",2));
      tc1.add(new TaggedCodePiece(".getX();\n"));
      tc1.add(new TaggedCodePiece("y",1));
      tc1.add(new TaggedCodePiece(" = "));
      tc1.add(new TaggedCodePiece("point",2));
      tc1.add(new TaggedCodePiece(".getY();\n"));
      ///Create a piece of code that can be called
      LinkedList<TaggedCodePiece> tc2 = new LinkedList<TaggedCodePiece>();
      tc2.add(new TaggedCodePiece("System.out.println("));
      tc2.add(new TaggedCodePiece("s",0));
      tc2.add(new TaggedCodePiece(");\n"));
      LinkedList<VarBlock> p1 = new LinkedList<VarBlock>();
      LinkedList<VarBlock> r1 = new LinkedList<VarBlock>();
      LinkedList<VarBlock> p2 = new LinkedList<VarBlock>();
      LinkedList<VarBlock> r2 = null;
      LinkedList<RefBlock> r3 = null;
      try
      {
         p1.add(new VarBlock("point", null, 1, VarTypes.INT));
         r1.add(new VarBlock("x", null, 1, VarTypes.INT));
         r1.add(new VarBlock("y", null, 1, VarTypes.INT));
         p2.add(new VarBlock("s", null, 1, VarTypes.INT));
         cb1 = new CodeBlock("Code __Block 1^%", null, 1, null, p1, r1);
         cb2 = new CodeBlock("println", null, 2, null, p2, r2, "System.out.");
         cb3 = new CodeBlock("println", null, 3, null, p2, r2);
         cb4 = new CodeBlock("emptyMethod",null,4,null,null,null);
         cb1.setTaggedCode(tc1);
         cb3.setTaggedCode(tc2);
         cb4.setTaggedCode(new LinkedList<TaggedCodePiece>());
         
         //RefBlock rb[] = {new RefBlock(5,cb2), new RefBlock(6,cb3), new RefBlock(7,cb4)};
         
         AccessTypes[] cb3Access = {AccessTypes.FINAL, AccessTypes.STATIC}; 
         cb3.setAccess(cb3Access);
         cb3Access[0] = AccessTypes.PUBLIC; 
         cb3.setAccess(cb3Access);
         
         RefBlock rb1 = new RefBlock(10, cb1);
         RefBlock rb2 = new RefBlock(11, cb2);
//         RefBlock rb3 = new RefBlock(12, cb3);
         RefBlock rb4 = new RefBlock(13, cb4);
         RefBlock rb5 = new RefBlock(14, cb1);
         RefBlock rb6 = new RefBlock(15, cb2);
         RefBlock rb7 = new RefBlock(16, cb3);
         RefBlock rb8 = new RefBlock(17, cb4);

         RefBlock[] bc1 = {rb1, rb2};
//         RefBlock[] bc2 = {rb3};
         RefBlock[] bc3 = {rb4, rb8};
         RefBlock[] bc4 = {rb5, rb6, rb7};
    
         bb1 = new BlockBlock("Block Block 1", null, 20, CallTypes.INLINE);
         bb2 = new BlockBlock("Block Block 2", null, 21, cb3Access, p1, r3);
         bb3 = new BlockBlock("Block Block 3", null, 22, cb3Access, p2, r3);
         bb4 = new BlockBlock("Block Block 4", null, 23, CallTypes.INLINE);
         bb5 = new BlockBlock("Block Block 5", null, 24, CallTypes.METHOD);
         
         bb1.setAccess(null);
         bb4.setAccess(null);
         bb1.addParam(p1.get(0));
         bb1.addReturn(new RefBlock(50,r1.get(0)));
         bb1.addReturn(new RefBlock(51,r1.get(1)));
         bb4.addReturn(new RefBlock(52,r1.get(0)));
         bb4.addReturn(new RefBlock(53,r1.get(1)));
         
         VarBlock vb = new VarBlock("PrintMe!",null,54,VarTypes.INT, true);
         vb.setValue("11", 1);
         
         bb5.setAccess(null);
         bb5.addParam(vb);

         RefBlock rb9 = new RefBlock(18, bb1);
         RefBlock rb10 = new RefBlock(19, bb2);
         RefBlock rb11 = new RefBlock(77, bb5);
         rb11.addParam(new RefBlock(78, vb));
         
         //bc3.add(rb9);
         //bc4.add(2,rb10);
         
         bb1.addCode(bc1, 0);
         bb2.addCode(rb11);
         bb3.addCode(bc3, 0);
         bb4.addCode(bc4, 0);
         bb3.addCode(rb9);
         bb4.addCode(rb10,2);
         bb1.addVar(new VarBlock("vb1", null, 42, VarTypes.BOOLEAN, null, 4) );
         
         
         bb2.getCode().get(0).addParam(new RefBlock(55, vb));

         System.out.println(bb1);
         System.out.println(bb2);
         System.out.println(bb3);
         System.out.println(bb4);
         
         //bb2.checkError();
         rb10.update();
         System.out.println("rb10Error = " + rb10.hasError());
         System.out.println("bb2Error = " + bb2.hasError());
         System.out.println("bb5Error = " + bb5.hasError());
         System.out.println("bb4Error = " + bb4.hasError());
         
      }
      catch(Exception e)
      {
         System.err.println(e.getMessage());
         return;
      }
      
      
   }

}
